package com.example.springsecurity06.config;

import com.fasterxml.jackson.databind.ObjectMapper;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.session.FindByIndexNameSessionRepository;
import org.springframework.session.data.redis.config.annotation.web.http.EnableRedisHttpSession;
import org.springframework.session.data.redis.config.annotation.web.http.EnableRedisIndexedHttpSession;
import org.springframework.session.security.SpringSessionBackedSessionRegistry;

import java.util.HashMap;

@Configuration
@EnableWebSecurity
@EnableRedisIndexedHttpSession
public class SecurityConfig {
    @Autowired
    private FindByIndexNameSessionRepository findByIndexNameSessionRepository;

//    private final FindByIndexNameSessionRepository findByIndexNameSessionRepository;

//    @Autowired
//    public SecurityConfig(FindByIndexNameSessionRepository findByIndexNameSessionRepository) {
//        this.findByIndexNameSessionRepository = findByIndexNameSessionRepository;
//    }

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity) throws Exception {
        return httpSecurity.authorizeHttpRequests(authorizationManagerRequestMatcherRegistry -> {
            authorizationManagerRequestMatcherRegistry.anyRequest().authenticated();
        }).formLogin(httpSecurityFormLoginConfigurer -> {
        }).csrf(httpSecurityCsrfConfigurer -> {
//            httpSecurityCsrfConfigurer.disable();
        }).sessionManagement(httpSecuritySessionManagementConfigurer -> {
            httpSecuritySessionManagementConfigurer.maximumSessions(1) // 开启会话管理，并且设置会话并发数为1，此时后登录者为主，刷新先登录页面会提示超过会话数限制了
//                    .expiredUrl("/login"); // 传统web开发中，会话过期后跳转到login页面
        // SessionInformationExpiredStrategy是一个接口，且其只有一个方法，在jdk1.8以后可以叫做函数式接口，可通过匿名内部类实现（完整写法是通过new SessionInformationExpiredStrategy()实现），由于只有一个参数event因此可以通过event快速实现
                    .expiredSessionStrategy(event -> {
                        HttpServletResponse response = event.getResponse();
                        response.setContentType("application/json;charset=utf-8"); //注意：它需要写在 response.getWriter()之前，才能正常显示提示文字，否则会乱码
                        HashMap<String, Object> resultMap = new HashMap<>();
                        resultMap.put("msg", "当前会话已失效，请重新登录");
                        resultMap.put("status", 500);
                        String resultStr = new ObjectMapper().writeValueAsString(resultMap);
                        response.getWriter().println(resultStr);
                        response.flushBuffer(); // flushBuffer()会强行把Buffer的 内容写到客户端浏览器
                    }) // 前后端分离开发的方式
                    .sessionRegistry(sessionRegistry());  // 设置集群环境下，将会话内容存储到redis中，进行会话共享
//                    .maxSessionsPreventsLogin(true); // 禁止登录之后再次登录，防止后登录的B把先登录的A剔除下
        }).build();
    }

//    使用它，则注册掉下面自带的httpSessionEventPublisher()监听
    @Bean
    public SpringSessionBackedSessionRegistry sessionRegistry() {
        return new SpringSessionBackedSessionRegistry(findByIndexNameSessionRepository);
    }

//   进行监听，通过map管理当前的httpsession记录，从而实现会话的并发管理，虽然不添加也可以正常实现，但是博主建议还是加上
//    @Bean
//    public HttpSessionEventPublisher httpSessionEventPublisher() {
//        return new HttpSessionEventPublisher();
//    }
}
